#!/usr/bin/env python
# encoding: utf-8
"""
@author: youfeng
@email: youfeng243@163.com
@license: Apache Licence 
@file: queue_mq_thread.py
@time: 2016/12/8 13:45
"""
import json
import threading
import time
from Queue import Queue

from beanstalkc import SocketError, CommandFailed

from common.pybeanstalk import PyBeanstalk


class SchedulerHandler(threading.Thread):
    PAUSE_BLOCK_TIME = 10

    PAUSE_NORMAL_TIME = 1

    # 最大待发送队列大小
    MAX_QUEUE_SIZE = 20

    def __init__(self, beanstalk_conf, tube_set, max_block=20, log=None):
        threading.Thread.__init__(self)
        self.daemon = True

        self.log = log

        # 输送队列
        self.queue_dict = {key: Queue() for key in tube_set}

        # 最大阻塞数目
        self.__max_block = max_block

        # 获取消息队列配置
        self.server_conf = beanstalk_conf

        self.__beanstalk = PyBeanstalk(self.server_conf['host'], self.server_conf['port'])

        # 设置线程运行
        self.__is_running = True

        # 直接启动线程
        self.start()

    # 判断beanstalk消息队列是否堵塞
    def __is_beanstalk_block(self, tube):
        try:
            count = self.__beanstalk.get_tube_count(tube)
        except CommandFailed, (_, status, results):
            if status == 'NOT_FOUND':
                self.log.error('获取消息队列不存在: tube = {}'.format(tube))
                count = 0
            else:
                self.log.error('获取消息队列异常: tube = {} status = {}'.format(tube, status))
                count = self.__max_block
        except Exception as e:
            count = self.__max_block
            self.log.error("获取消息队列大小失败: ")
            self.log.exception(e)

        if count >= self.__max_block:
            return True

        return False

    # # 判断是否需要暂停
    # def __wait_beanstalk_queue(self, tube):
    #     times = 0
    #     sleep_time = self.PAUSE_NORMAL_TIME
    #     while True:
    #
    #         if not self.__is_beanstalk_block(tube):
    #             return True
    #
    #         times += 1
    #         if times % 20 == 0:
    #             self.log.info("当前消息队列阻塞需要休眠: tube = {} sleep = {}".format(tube, sleep_time))
    #
    #         # 开始休眠
    #         time.sleep(sleep_time)

    # 判断内存队列是否有阻塞
    def is_mem_queue_block(self, tube):

        # 如果队列不存在 则永远不发送数据
        queue = self.queue_dict.get(tube)
        if queue is None:
            return True

        return self.__is_mem_queue_block(queue)

    def __is_mem_queue_block(self, queue):
        if queue.qsize() >= self.MAX_QUEUE_SIZE:
            return True

        return False

    def close(self):
        self.__is_running = False

    def push_msg(self, tube, item_dict):

        # 获得当前内存队列
        queue = self.queue_dict.get(tube)
        if queue is None:
            self.log.error("当前内存队列不存在: tube = {}".format(tube))
            return False

        # 判断当前内存队列是否堵塞
        if self.__is_mem_queue_block(queue):
            self.log.error("当前内存队列阻塞: tube = {}".format(tube))
            return False

        queue.put_nowait(json.dumps(item_dict))
        return True

    def run(self):
        self.log.info('开始运行消息队列...')
        while self.__is_running:

            is_all_empty = True
            for tube, queue in self.queue_dict.iteritems():
                try:
                    # 如果当前监听的队列为空 则直接跳过
                    if queue.qsize() <= 0:
                        continue

                    # 如果消息队列阻塞 则直接跳过
                    if self.__is_beanstalk_block(tube):
                        continue

                    is_all_empty = False

                    body = queue.get()

                    # # 如果是字符串则直接退出，视为退出消息
                    # if isinstance(body, basestring):
                    #     self.log.info("消息队列线程收到字符串数据, 退出线程: body = {}".format(body))
                    #     break
                    #
                    if not isinstance(body, basestring):
                        self.log.error("消息类型错误: body type = {} msg = {}".format(type(body), body))
                        continue

                    while True:
                        try:
                            # 发送前先判断是否需要休眠 如果阻塞则直接返回
                            # if not self.__wait_beanstalk_queue(tube, block):
                            #     break

                            result = self.__beanstalk.put(tube, body)
                            if result is None:
                                self.log.error("数据量太大: msg len = {}".format(len(body)))

                            break
                        except SocketError as e:
                            time.sleep(10)
                            self.__beanstalk.reconnect()
                            self.log.warn("reconnect beanstalk...")
                            self.log.exception(e)
                        except Exception as e:
                            self.log.error('捕获异常休眠:')
                            self.log.exception(e)
                            time.sleep(10)
                except Exception as e:
                    self.log.info('当前队列大小: size = {size}'.format(size=queue.qsize()))
                    self.log.exception(e)
                    time.sleep(5)

            # 如果全是空的 则休眠1s
            if is_all_empty:
                time.sleep(1)

        self.log.info("消息队列正常退出...")
